/**
******************************************************************************
* @file    netbios.c
* @author  WIZnet Software Team 
* @version V1.0
* @date    2015-02-14
* @brief   nebios 名字解析服务器功能    通过Ping	NETBIOS_W5500_NAME 可获得其IP地址
* @attention  
******************************************************************************
*/
#include <string.h>
#include <stdio.h>

#include "W5500_Driver.h"
#include "socket.h"
#include "utility.h"
#include "netbios.h"

#define NETBIOS_W5500_NAME "W5500"		/* 定义NetBIOS名字*/

#define NETBIOS_SOCK     6																	/* 定义NetBIOS采用的socket*/
#define NETBIOS_PORT     137																/* "NetBIOS Name service"的默认端口 */


#define NETBIOS_NAME_LEN 16																	/*NetBIOS名字最大长度*/

#define NETBIOS_MSG_MAX_LEN 512															/*NetBIOS报文的最大长度 */

#define NETBIOS_NAME_TTL 10//300000													/*NetBIOS响应时间*/

/** NetBIOS header flags */
#define NETB_HFLAG_RESPONSE           0x8000U
#define NETB_HFLAG_OPCODE             0x7800U
#define NETB_HFLAG_OPCODE_NAME_QUERY  0x0000U
#define NETB_HFLAG_AUTHORATIVE        0x0400U
#define NETB_HFLAG_TRUNCATED          0x0200U
#define NETB_HFLAG_RECURS_DESIRED     0x0100U
#define NETB_HFLAG_RECURS_AVAILABLE   0x0080U
#define NETB_HFLAG_BROADCAST          0x0010U
#define NETB_HFLAG_REPLYCODE          0x0008U
#define NETB_HFLAG_REPLYCODE_NOERROR  0x0000U

/** NetBIOS name flags */
#define NETB_NFLAG_UNIQUE             0x8000U
#define NETB_NFLAG_NODETYPE           0x6000U
#define NETB_NFLAG_NODETYPE_HNODE     0x6000U
#define NETB_NFLAG_NODETYPE_MNODE     0x4000U
#define NETB_NFLAG_NODETYPE_PNODE     0x2000U
#define NETB_NFLAG_NODETYPE_BNODE     0x0000U

/** NetBIOS message header */
#pragma pack(1)
typedef struct _NETBIOS_HDR {
  uint16 trans_id;
  uint16 flags;
  uint16 questions;
  uint16 answerRRs;
  uint16 authorityRRs;
  uint16 additionalRRs;
}NETBIOS_HDR;


/** NetBIOS message name part */
typedef struct _NETBIOS_NAME_HDR {
  uint8  nametype;
  uint8  encname[(NETBIOS_NAME_LEN*2)+1];
  uint16 type;
  uint16 cls;
  uint32 ttl;
  uint16 datalen;
  uint16 flags;
  //ip_addr_p_t addr;
  uint8 addr[4];
}NETBIOS_NAME_HDR;


/** NetBIOS 报文结构体 */
typedef struct _NETBIOS_RESP
{
  NETBIOS_HDR      resp_hdr;
  NETBIOS_NAME_HDR resp_name;
}NETBIOS_RESP;
#pragma pack()

 /**
*@brief		  NetBIOS解码
*@param		  *name_enc			解码前的NetBios名字
*@param		  *name_dec			解码后的NetBios名字
*@param		  name_dec_len
*@return	  无
*/
static int netbios_name_decoding(char *name_enc, char *name_dec, int name_dec_len)
{
	char *pname;
	char  cname;
	char  cnbname;
	int   index = 0;

	/* 启动NetBios解码 */
	pname  = name_enc;
	for (;;) {
		/* Every two characters of the first level-encoded name
		 * turn into one character in the decoded name. */
		cname = *pname;
		if (cname == '\0')
			break;    /* no more characters */
		if (cname == '.')
			break;    /* scope ID follows */
		if (cname < 'A' || cname > 'Z') {
			/* Not legal. */
			return -1;
		}
		cname -= 'A';
		cnbname = cname << 4;
		pname++;

		cname = *pname;
		if (cname == '\0' || cname == '.') {
			/* No more characters in the name - but we're in
			 * the middle of a pair.  Not legal. */
			return -1;
		}
		if (cname < 'A' || cname > 'Z') {
			/* Not legal. */
			return -1;
		}
		cname -= 'A';
		cnbname |= cname;
		pname++;

		/* Do we have room to store the character? */
		if (index < NETBIOS_NAME_LEN) {
			/* Yes - store the character. */
			name_dec[index++] = (cnbname != ' ' ? cnbname : '\0');
		}
	}
	return 0;
}

char netbios_rx_buf[NETBIOS_MSG_MAX_LEN];
char netbios_tx_buf[NETBIOS_MSG_MAX_LEN];
 /**
*@brief		  执行NetBIOS name解析程序 
*@param		  无
*@return	  无
*/
void do_netbios(void)
{
	unsigned char state;
	unsigned int len;
	state = getSn_SR(NETBIOS_SOCK);
	switch (state)
	{
	case SOCK_UDP:
		if ((len = getSn_RX_RSR(NETBIOS_SOCK)) > 0)
		{
			unsigned char rem_ip_addr[4];
			uint16 rem_udp_port;
			char netbios_name[NETBIOS_NAME_LEN + 1];
			NETBIOS_HDR* netbios_hdr;
			NETBIOS_NAME_HDR* netbios_name_hdr;
			len = recvfrom(NETBIOS_SOCK, (unsigned char*)&netbios_rx_buf, len, rem_ip_addr, &rem_udp_port);
			printf("rem_ip_addr=%d.%d.%d.%d:%d\r\n", rem_ip_addr[0], rem_ip_addr[1], rem_ip_addr[2], rem_ip_addr[3], rem_udp_port);
			netbios_hdr = (NETBIOS_HDR*)netbios_rx_buf;
			netbios_name_hdr = (NETBIOS_NAME_HDR*)(netbios_hdr + 1);
			/* 如果数据包是NetBIOS查询包*/
			if (((netbios_hdr->flags & ntohs(NETB_HFLAG_OPCODE)) == ntohs(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
					((netbios_hdr->flags & ntohs(NETB_HFLAG_RESPONSE)) == 0) &&
					 (netbios_hdr->questions == ntohs(1))) 
			{
				printf("netbios name query question\r\n");
				/* 对NetBIOS包进行解码 */
				netbios_name_decoding((char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
				printf("name is %s\r\n", netbios_name);
				/* 如果是针对本机的NetBIOS查询 */
				if (strcmp(netbios_name, NETBIOS_W5500_NAME) == 0) 
				{
					uint8 ip_addr[4];
					NETBIOS_RESP *resp = (NETBIOS_RESP*)netbios_tx_buf;
					/* 处理NetBIOS响应包的header*/
					resp->resp_hdr.trans_id      = netbios_hdr->trans_id;
					resp->resp_hdr.flags         = htons(NETB_HFLAG_RESPONSE |
														 NETB_HFLAG_OPCODE_NAME_QUERY |
														 NETB_HFLAG_AUTHORATIVE |
														 NETB_HFLAG_RECURS_DESIRED);
					resp->resp_hdr.questions     = 0;
					resp->resp_hdr.answerRRs     = htons(1);
					resp->resp_hdr.authorityRRs  = 0;
					resp->resp_hdr.additionalRRs = 0;

					/* 处理NetBIOS响应包的header数据*/
					memcpy(resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));
					resp->resp_name.nametype     = netbios_name_hdr->nametype;
					resp->resp_name.type         = netbios_name_hdr->type;
					resp->resp_name.cls          = netbios_name_hdr->cls;
					resp->resp_name.ttl          = htonl(NETBIOS_NAME_TTL);
					resp->resp_name.datalen      = htons(sizeof(resp->resp_name.flags) + sizeof(resp->resp_name.addr));
					resp->resp_name.flags        = htons(NETB_NFLAG_NODETYPE_BNODE);
					getSIPR(ip_addr);
					memcpy(resp->resp_name.addr, ip_addr, 4);
												
					/* 发送NetBIOS响应 */
					sendto(NETBIOS_SOCK, (unsigned char*)resp, sizeof(NETBIOS_RESP), rem_ip_addr, rem_udp_port);
					printf("send response\r\n");
				}
			}
		}
		break;
			
	case SOCK_CLOSED:
		close(NETBIOS_SOCK);
		socket(NETBIOS_SOCK, Sn_MR_UDP, NETBIOS_PORT, 0);
		break;
		
	default:
		break;
	}
}

